/**尾调用（Tail Call）的含义就是在函数A的最后一步调用函数B
注意一定是最后一步，这是可以通过合理的安排函数A的结构体实现的！
原理：代码执行时基于执行栈的，所以在一个函数中调用另一个函数的时候，会保留调用者A的执行上下文（保留直到函数A执行完毕！），然后将被调用的函数B的执行上下文加入到执行栈中
显而易见的是，如果函数之间的相互调用层数过深，就会导致很多的函数执行上下文都被加入到执行栈中，导致栈的溢出！
采用尾调用优化之后的函数，根据定义，函数A只有可能在最后一步才调用函数B，既然是最后一步那么js引擎就无需将A的执行上下文保存在执行栈中了！
于是节省了内存
需要注意的是：尾调用优化只有在严格模式下才会生效

以斐波那契数列举例子，一般写法，
 * 
 * @param {*} n 
 * @returns 
 */
function factorial(n) {
  if (n <= 1) {
    return 1;
  }
  // 非尾调用：递归调用后仍需乘以 n，因此factorial函数要等下一个执行完
  return n * factorial(n - 1);
}

// 结果作为参数传进
function factorial2(n, res = 1) {
  if (n <= 1) {
    return res;
  }
  // 尾调用优化：传递累积的结果而不进行额外的乘法操作
  return factorial2(n - 1, res * n);
}

// eg1
let res = factorial(6);
let res2 = factorial2(6);
console.log("[ res ] >", res);
console.log("[ res2 ] >", res2);
